package test.netease.version2.chapter1.lesson1_1_5;

import java.util.concurrent.locks.LockSupport;

/**
 * @author zhangrui
 * @date 2020-04-24 20:51
 * @since 1.0
 */
public class ThreadCommunicaion {
    private Object baozi = null;

    //suspend,resume
    public void suspendResumeTest() throws Exception {
        Thread consumerThread = new Thread(() -> {
            if (baozi == null) {
                System.out.println("1.进入等待");
                //suspend挂起
                Thread.currentThread().suspend();
            }
            System.out.println("3.买到包子，回家");
        });
        consumerThread.start();

        //3秒之后，生产一个包子
        Thread.sleep(3000);

        baozi = new Object();

        consumerThread.resume();

        System.out.println("3.通知消费者");
    }

    //suspend在挂起之后，并不会释放锁，故此容易写出死锁的代码
    //sleep方法也不会释放锁
    public void suspendResumeDeadLockTest() throws Exception {
        Thread consumerThread = new Thread(() -> {
            if (baozi == null) {
                System.out.println("1.进入等待");

                //当前线程拿到锁，然后挂起
                synchronized (this) {
                    Thread.currentThread().suspend();
                }
            }
            System.out.println("3.买到包子，回家");
        });
        consumerThread.start();

        //3秒之后，生产一个包子
        Thread.sleep(3000);

        baozi = new Object();

        //想拿到锁以后，再恢复consumerThread，但锁仍然被thread1占用着，于是死锁
        synchronized (this) {
            consumerThread.resume();
        }

        System.out.println("3.通知消费者");
    }

    //必须先执行suspend，再执行resume，反之则死锁
    public void suspendResumeDeadLockTest2() throws Exception {
        Thread consumerThread = new Thread(() -> {
            if (baozi == null) {
                System.out.println("1.进入等待");

                //为这个线程加上一点时间，确保suspend执行在主线程的resume之后
                try {
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                Thread.currentThread().suspend();
            }
            System.out.println("3.买到包子，回家");
        });
        consumerThread.start();

        //3秒之后，生产一个包子
        Thread.sleep(3000);

        baozi = new Object();

        //主线程先执行resume
        consumerThread.resume();

        System.out.println("3.通知消费者");

    }


    public void waitNotifyTest() throws Exception {
        Thread consumerThread = new Thread(() -> {
            if (baozi == null) {
                synchronized (this) {
                    System.out.println("1.进入等待");
                    try {
                        this.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
            System.out.println("3.买到包子，回家");
        });
        consumerThread.start();

        //3秒之后，生产一个包子
        Thread.sleep(3000);

        synchronized (this) {

            baozi = new Object();
            System.out.println("3 开始生产包子");

            this.notifyAll();
            System.out.println("4.通知消费者");
        }

    }

    //必须先执行wait，再执行notify，反之会陷入无限等待
    public void waitNotifyForeverWaitTest() throws Exception {
        Thread consumerThread = new Thread(() -> {
            if (baozi == null) {

                //给这个线程加点时间，确保主线程先拿到锁，进入同步块，执行notifyAll
                try {
                     Thread.sleep(5000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                synchronized (this) {

                    System.out.println("1.进入等待");
                    try {
                        /*
                        我起初的误区：等thread1进入同步块了，才执行sleep。
                        这时consumerThread已经拿到锁，主线程的同步块没有拿到锁，
                        所以notfiyAll还是会执行在wait之后，于是无限等待就不会出现
                         */
                        //Thread.sleep(5000);

                        this.wait();

                        System.out.println("aaaa");
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
            System.out.println("4.买到包子，回家");
        });
        consumerThread.start();

        //3秒之后，生产一个包子
        Thread.sleep(1000);

        //一定要
        synchronized (this) {

            baozi = new Object();
            System.out.println("2 开始生产包子");

            /*
            notify并不释放锁，只是告诉调用过wait方法的线程可以去参与获得锁的竞争了，
            但不是马上得到锁，因为锁还在别人手里，别人还没释放
             */
            this.notifyAll();
            System.out.println("3 通知消费者");
        }

    }

    public void parkUnparkTest() throws Exception {
        Thread consumerThread = new Thread(() -> {
            if (baozi == null) {
                System.out.println("1.进入等待");
                //等待许可
                LockSupport.park();
            }
            System.out.println("3.买到包子，回家");
        });
        consumerThread.start();

        //3秒之后，生产一个包子
        Thread.sleep(3000);

        baozi = new Object();

        //通知
        LockSupport.unpark(consumerThread);

        System.out.println("2.通知消费者");
    }

    // park/unpark没有顺序要求，但是park并不会释放锁，
    // 所以，所有再同步代码中使用要注意不要把它们写在，进入时还需要锁的同步块里，否则也可能出现死锁
    public void parkUnparkDeadLockTest() throws Exception {
        Thread consumerThread = new Thread(() -> {
            if (baozi == null) {
                System.out.println("1.进入等待");
                synchronized (this) {
                    LockSupport.park();
                }
            }
            System.out.println("3.买到包子，回家");
        });
        consumerThread.start();

        //3秒之后，生产一个包子
        Thread.sleep(3000);

        baozi = new Object();

        synchronized (this) {
            LockSupport.unpark(consumerThread);
        }

        System.out.println("2.通知消费者");
    }

    public static void main(String[] args) throws Exception {
        ThreadCommunicaion tc = new ThreadCommunicaion();

        //tc.suspendResumeDeadLockTest2();
        //tc.waitNotifyTest();
        //tc.waitNotifyForeverWaitTest();
        //tc.parkUnparkTest();
        tc.parkUnparkDeadLockTest();
    }
}
